package com.lzp.java.concurrent.deadlock;

import java.util.concurrent.TimeUnit;

/**
 * 转账死锁，使用死锁避免策略--解决锁顺序问题
 *
 * @author lzp
 * @date 2020/03/04
 */
public class TransferMoneyNotDeadLock implements Runnable {
    private int flag;

    public TransferMoneyNotDeadLock(int flag) {
        this.flag = flag;
    }

    /**
     * 两个账户锁
     */
    private static Account account1 = new Account(500);
    private static Account account2 = new Account(500);

    /**
     * 额外的锁，用于hashcode相同时规范获取锁顺序
     */
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread thread0 = new Thread(new TransferMoneyNotDeadLock(0));
        Thread thread1 = new Thread(new TransferMoneyNotDeadLock(1));
        thread0.start();
        thread1.start();
        thread0.join();
        thread1.join();
        System.out.println("账户1余额：" + account1.blance);
        System.out.println("账户2余额：" + account2.blance);
    }

    @Override
    public void run() {
        if (flag == 0) {
            transMoney(account1, account2, 200);
        } else {
            transMoney(account2, account1, 200);
        }
    }

    private void transMoney(Account from, Account to, int account) {
        // 获取两个账户的hashcode，通过比较大小规范获取锁的顺序
        int fromHashCode = System.identityHashCode(from);
        int toHashCode = System.identityHashCode(to);
        if (fromHashCode < toHashCode) {
            synchronized (from) {
                synchronized (to) {
                    transfer(from, to, account);
                }
            }
        } else if (fromHashCode > toHashCode) {
            synchronized (to) {
                synchronized (from) {
                    transfer(from, to, account);
                }
            }
        } else { // 此时hashcode值相同
            synchronized (lock) {
                synchronized (from) {
                    synchronized (to) {
                        transfer(from, to, account);
                    }
                }
            }
        }
    }

    private void transfer(Account from, Account to, int account) {
        if (from.blance < account) {
            throw new RuntimeException("账户余额不足");
        }
        from.blance -= account;
        to.blance += account;
        System.out.println("成功转账" + account + "元");
    }

    static class Account {
        int blance;

        public Account(int blance) {
            this.blance = blance;
        }
    }
}
